package com.gitee.fantaros.web.fan.netcut.views;

import com.gitee.fantaros.web.fan.netcut.service.DeepSeekService;
import com.gitee.fantaros.web.fan.netcut.threading.Presentable;
import com.vaadin.flow.component.HasOrderedComponents;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.html.NativeCode;
import com.vaadin.flow.component.html.Paragraph;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.Scroller;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.router.*;
import com.vaadin.flow.server.VaadinSession;
import com.vaadin.flow.spring.annotation.UIScope;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.BufferedReader;
import java.io.StringReader;
import java.util.List;

/**
 * 内容
 */
@Route("/deepseek")
@PageTitle("DeepSeek 连接前端 by fantaros")
@UIScope
@Slf4j
public class DeepSeekView extends VerticalLayout implements BeforeEnterObserver, AfterNavigationObserver {
    @Getter
    private final TextArea ask;
    @Getter
    private final VerticalLayout answer;
    @Getter
    private final Image loadingImage;
    @Getter
    private final Button askButton;
    @Getter
    private final Paragraph details;
    @Getter
    private String token;

    private final DeepSeekService deepSeekService;

    public DeepSeekView(@Autowired DeepSeekService deepSeekService) {
        this.deepSeekService = deepSeekService;
        //head
        Div head = new Div();
        head.setWidth("100%");
        head.setHeight("1%");
        //content
        Div content = new Div();
        content.setWidth("100%");
        content.setHeight("80%");
        content.getStyle().set("border-radius", "3px").setBorder("1px solid black").setBackground("lightgray");
        Div scroll = new Div();
        scroll.setWidth("100%");
        scroll.setHeight("100%");
        scroll.getStyle().set("overflow-y", "scroll");
        this.answer = new VerticalLayout();
        this.answer.setWidth("100%");
        this.answer.setHeight("100%");
        scroll.add(this.answer);
        content.add(scroll);
        //footer
        Div footer = new Div();
        footer.setWidth("100%");
        footer.setHeight("18%");

        this.details = new Paragraph();
        this.details.setWidth("100%");
        this.details.setHeight("24px");
        this.details.setMinHeight("24px");
        this.details.getStyle().setFont("bold 18px san-serif");
        footer.add(this.details);

        HorizontalLayout functionBar = new HorizontalLayout();
        functionBar.setWidth("100%");
        functionBar.setHeight("10%");
        this.ask = new TextArea();
        this.ask.setWidth("75%");
        this.ask.setMinWidth("200px");
        this.ask.setHeight("100%");
        this.ask.setMinHeight("64px");
        this.ask.getStyle().set("border-radius", "3px").setBorder("1px dash black").setBackground("lightyellow");
        this.ask.addKeyDownListener(event -> {
            if (event.getKey() == Key.ENTER && this.ask.getValue() != null && !this.ask.getValue().trim().isEmpty()) {
                doAsk();
            }
        });
        functionBar.add(this.ask);
        this.askButton = new Button();
        this.askButton.setText("DeepSeek");
        this.askButton.setWidth("25%");
        this.askButton.setMinWidth("80px");
        this.askButton.setHeight("90%");
        this.askButton.setMinHeight("32px");
        this.askButton.addClickListener(e -> doAsk());
        this.loadingImage = new Image("images/loading.gif", "0");
        this.loadingImage.setWidth("32px");
        this.loadingImage.setHeight("32px");
        this.loadingImage.setVisible(false);
        functionBar.add(askButton);
        functionBar.add(loadingImage);
        footer.add(functionBar);
        //UI skeleton
        add(head);
        add(content);
        add(footer);
        setWidth("100%");
        setHeight("100%");
    }

    private void doAsk() {
        this.loadingImage.setVisible(true);
        this.askButton.setVisible(false);
        this.deepSeekService.asyncSendAsk(this.ask.getValue(), this::getValue, this::process);
    }

    private String getValue(Presentable<String> valueWrapper, Presentable<Exception> anyException) throws Exception {
        if (anyException.isPresent()) {
            appendContents(anyException.get());
            return "与服务器连接异常";
        }
        String result = valueWrapper.isEmpty() ? "从服务器获得了空结果" : valueWrapper.get();
        deepSeekService.insertContent(this.token, result);
        this.getUI().ifPresent(ui -> ui.access(() -> {
            DeepSeekView that = (DeepSeekView) ui.getCurrentView();
            that.getDetails().setText("");
            appendContents(that.getAnswer(), result);
            that.getLoadingImage().setVisible(false);
            that.getAskButton().setVisible(true);
            ui.push();
        }));
        return result;
    }

    private void appendContents(Exception e) {
        if (e == null) {
            return;
        }
        addResultP(answer, e.getLocalizedMessage());
    }

    private void process(String message) {
        this.getUI().ifPresent(ui -> ui.access(() -> {
            DeepSeekView that = (DeepSeekView) ui.getCurrentView();
            that.getDetails().setText(message);
            ui.push();
        }));
    }

    private void appendContents(HasOrderedComponents answer, String value) {
        if (value.contains("<think>")) {
            int thinkIndex = value.indexOf("</think>");
            String thinkPresents = value.substring(7, thinkIndex).trim();
            if (!thinkPresents.isEmpty()) {
                NativeCode thinkP = new NativeCode();
                thinkP.setWidth("100%");
                thinkP.getStyle().set("color", "lightgray").set("background-color", "darkgray");
                addInnerContent(thinkP, "思考过程:".concat(value.substring(7, thinkIndex)));
                answer.addComponentAsFirst(thinkP);
            }
            addResultP(answer, value.substring(thinkIndex + 8));
        } else {
            addResultP(answer, value);
        }
    }

    private void addResultP(HasOrderedComponents answer, String value) {
        NativeCode result = new NativeCode();
        result.setWidth("100%");
        result.getStyle().set("font-color", "black");
        addInnerContent(result, value);
        answer.addComponentAsFirst(result);
    }

    private void addInnerContent(NativeCode code, String value) {
        if (value == null || value.trim().isEmpty()) {
            return;
        }
        BufferedReader bufferedReader = new BufferedReader(new StringReader(value));
        String line = null;
        try {
            while ((line = bufferedReader.readLine()) != null) {
                Paragraph p = new Paragraph();
                p.setWidth("100%");
                p.setText(line);
                code.add(p);
            }
        } catch (Exception ignored) {

        }
    }

    private String replaceWithBr(String content) {
        return content.replaceAll("\\n", "<br/>");
    }


    @Override
    public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
        this.ask.setValue("");
        this.answer.removeAll();
        deepSeekService.prepareTables();
//        this.loadingImage.setVisible(false);
    }


    @Override
    public void afterNavigation(AfterNavigationEvent afterNavigationEvent) {
        if (this.getUI().isPresent()) {
            VaadinSession session = this.getUI().get().getSession();
            this.token = session.getSession().getId();
        } else {
            this.token = null;
        }
        if (this.token != null) {
            refreshAllInToken(deepSeekService.selectContents(this.token));
        }
    }

    private void refreshAllInToken(List<String> contents) {
        if (contents != null && !contents.isEmpty()) {
            contents.forEach(c -> appendContents(this.getAnswer(), c));
        }
    }
}
